package org.huzhp.oauth.client.shiro;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.oltu.oauth2.client.OAuthClient;
import org.apache.oltu.oauth2.client.URLConnectionClient;
import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
import org.apache.oltu.oauth2.client.response.OAuthAccessTokenResponse;
import org.apache.oltu.oauth2.client.response.OAuthResourceResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.huzhp.oauth.client.Oauth2Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;

public class Oauth2Realm extends AuthorizingRealm {

    private static final Logger logger = LoggerFactory.getLogger(Oauth2Realm.class);


    private Oauth2Properties oauth2Properties;


    public Oauth2Realm(Oauth2Properties oauth2Properties) {
        this.oauth2Properties = oauth2Properties;
    }

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof Oauth2Token; //表示此Realm只支持OAuth2Token类型
    }


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        Oauth2Token oAuth2Token = (Oauth2Token) token;
        return new SimpleAuthenticationInfo(extractUsername(oAuth2Token), oAuth2Token.getCredentials(), getName());

    }

    private Map extractUsername(Oauth2Token oauth2Token) {
        OAuthResourceResponse resourceResponse;
        String accessToken;
        try {
            OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());

            OAuthClientRequest accessTokenRequest = OAuthClientRequest
                    .tokenLocation(oauth2Properties.getAccessTokenUrl())
                    .setGrantType(GrantType.AUTHORIZATION_CODE)
                    .setClientId(oauth2Properties.getClientId())
                    .setClientSecret(oauth2Properties.getClientSecret())
                    .setCode((String) oauth2Token.getCredentials())
                    .setRedirectURI(oauth2Properties.getRedirectUri())
                    .buildBodyMessage();

            OAuthAccessTokenResponse oAuthResponse = oAuthClient.accessToken(accessTokenRequest, OAuth.HttpMethod.POST);

            accessToken = oAuthResponse.getAccessToken();

            OAuthClientRequest userInfoRequest = new OAuthBearerClientRequest(oauth2Properties.getUserinfoUrl())
                    .setAccessToken(accessToken).buildQueryMessage();

            resourceResponse = oAuthClient.resource(userInfoRequest, OAuth.HttpMethod.GET, OAuthResourceResponse.class);
            ObjectMapper mapper = new ObjectMapper();
            return mapper.readValue(resourceResponse.getBody(), Map.class);
        } catch (Exception e) {
            logger.error("调用oauth2接口出错,{},{}", oauth2Properties.getAccessTokenUrl(), oauth2Properties.getUserinfoUrl());
            throw new AuthenticationException(e);
        }

    }

}
